#include "stm32f1xx_hal.h"  // Include the appropriate header file according to the MCU model
#include <string.h>
#include <chfont.h>

// OLED related definitions
#define OLED_ADDR       0x78    // OLED I2C address
#define OLED_WIDTH      128     // OLED width
#define OLED_HEIGHT     64      // OLED height
#define OLED_PAGE_NUM   8       // OLED page number (64/8 = 8)

// OLED command definitions
#define OLED_CMD_SET_CONTRAST       0x81
#define OLED_CMD_DISPLAY_ON         0xAF
#define OLED_CMD_DISPLAY_OFF        0xAE
#define OLED_CMD_SET_MEMORY_MODE    0x20
#define OLED_CMD_SET_COLUMN_ADDR    0x21
#define OLED_CMD_SET_PAGE_ADDR      0x22

typedef struct {
    uint8_t width;
    uint8_t height;
    const uint8_t *data;
} ChineseFont;

const ChineseFont chinese_fonts[] = {
    {16, 16, (const uint8_t *)chinese_font_16},
    {24, 24, (const uint8_t *)chinese_font_24},
};
// Structure for Chinese character information
typedef struct {
    uint8_t font_index; // Index of the font in the font array
    uint8_t char_index; // Index of the character in the corresponding font data
} ChineseCharInfo;

// OLED function declarations
void OLED_Init(I2C_HandleTypeDef *hi2c);
void OLED_WriteCommand(I2C_HandleTypeDef *hi2c, uint8_t cmd);
void OLED_WriteData(I2C_HandleTypeDef *hi2c, uint8_t data);
void OLED_SetCursor(I2C_HandleTypeDef *hi2c, uint8_t x, uint8_t y);
void OLED_Clear(I2C_HandleTypeDef *hi2c);
void OLED_DisplayChar(I2C_HandleTypeDef *hi2c, uint8_t x, uint8_t y, char ch);
void OLED_DisplayChinese(I2C_HandleTypeDef *hi2c, uint8_t x, uint8_t y, ChineseCharInfo info);
void OLED_DisplayString(I2C_HandleTypeDef *hi2c, uint8_t x, uint8_t y, char *str);
void OLED_DisplayStringCentered(I2C_HandleTypeDef *hi2c, uint8_t line, char *str);
void OLED_DisplayChineseStringCentered(I2C_HandleTypeDef *hi2c, uint8_t line, ChineseCharInfo *chinese_info, uint8_t length);
ChineseCharInfo GetChineseIndex(char *str);

// OLED initialization function
void OLED_Init(I2C_HandleTypeDef *hi2c)
{
    // Delay to ensure OLED power stability
    HAL_Delay(100);

    // Send initialization commands
    OLED_WriteCommand(hi2c, OLED_CMD_DISPLAY_OFF);        // Turn off display
    OLED_WriteCommand(hi2c, 0x00);                          // Set low column address
    OLED_WriteCommand(hi2c, 0x10);                          // Set high column address
    OLED_WriteCommand(hi2c, 0x40);                          // Set start line address
    OLED_WriteCommand(hi2c, OLED_CMD_SET_CONTRAST);        // Set contrast control
    OLED_WriteCommand(hi2c, 0xCF);                          // Contrast value
    OLED_WriteCommand(hi2c, 0xA1);                          // Set segment re-map
    OLED_WriteCommand(hi2c, 0xC8);                          // Set COM output scan direction
    OLED_WriteCommand(hi2c, 0xA6);                          // Set normal display
    OLED_WriteCommand(hi2c, 0xA8);                          // Set multiplex ratio
    OLED_WriteCommand(hi2c, 0x3F);                          // Value is 64 - 1
    OLED_WriteCommand(hi2c, 0xD3);                          // Set display offset
    OLED_WriteCommand(hi2c, 0x00);                          // Offset value
    OLED_WriteCommand(hi2c, 0xD5);                          // Set display clock divide ratio/oscillator frequency
    OLED_WriteCommand(hi2c, 0x80);                          // Set divide ratio
    OLED_WriteCommand(hi2c, 0xD9);                          // Set pre - charge period
    OLED_WriteCommand(hi2c, 0xF1);                          // Pre - charge period value
    OLED_WriteCommand(hi2c, 0xDA);                          // Set COM pins hardware configuration
    OLED_WriteCommand(hi2c, 0x12);                          // COM pins hardware configuration value
    OLED_WriteCommand(hi2c, 0xDB);                          // Set VCOMH deselect level
    OLED_WriteCommand(hi2c, 0x40);                          // VCOMH deselect level value
    OLED_WriteCommand(hi2c, OLED_CMD_SET_MEMORY_MODE);     // Set memory addressing mode
    OLED_WriteCommand(hi2c, 0x00);                          // Horizontal addressing mode
    OLED_WriteCommand(hi2c, 0x8D);                          // Charge pump setting
    OLED_WriteCommand(hi2c, 0x14);                          // Enable charge pump
    OLED_WriteCommand(hi2c, OLED_CMD_DISPLAY_ON);          // Turn on display
    OLED_Clear(hi2c);                                        // Clear display
}

// Write command to OLED
void OLED_WriteCommand(I2C_HandleTypeDef *hi2c, uint8_t cmd)
{
    uint8_t data[2] = {0x00, cmd};  // 0x00 means command
    HAL_I2C_Master_Transmit(hi2c, OLED_ADDR, data, 2, 100);
}

// Write data to OLED
void OLED_WriteData(I2C_HandleTypeDef *hi2c, uint8_t data)
{
    uint8_t dataBuf[2] = {0x40, data};  // 0x40 means data
    HAL_I2C_Master_Transmit(hi2c, OLED_ADDR, dataBuf, 2, 100);
}

// Set cursor position
void OLED_SetCursor(I2C_HandleTypeDef *hi2c, uint8_t x, uint8_t y)
{
    OLED_WriteCommand(hi2c, 0xB0 + y);                  // Set page address
    OLED_WriteCommand(hi2c, ((x & 0xF0) >> 4) | 0x10);  // Set high column address
    OLED_WriteCommand(hi2c, (x & 0x0F) | 0x01);         // Set low column address
}

// Clear OLED display
void OLED_Clear(I2C_HandleTypeDef *hi2c)
{
    uint8_t i, j;
    for (i = 0; i < OLED_PAGE_NUM; i++)
    {
        OLED_SetCursor(hi2c, 0, i);
        for (j = 0; j < OLED_WIDTH; j++)
        {
            OLED_WriteData(hi2c, 0x00);
        }
    }
}

// Display ASCII character
void OLED_DisplayChar(I2C_HandleTypeDef *hi2c, uint8_t x, uint8_t y, char ch)
{
    uint8_t i, data;
    uint8_t char_index;

    // Ensure the character is within the displayable range (32 - 127 ASCII)
    if (ch < 32 || ch > 127)
        ch = ' ';  // Display space if the character is not displayable

    char_index = (uint8_t)ch - 32;  // Convert ASCII to font index

    // Display first page (0 - 7 rows)
    OLED_SetCursor(hi2c, x, y);
    for (i = 0; i < 8; i++) {
        data = oled_font[char_index][i];  // Get font data
        OLED_WriteData(hi2c, data);
    }

    // Display second page (8 - 15 rows)
    OLED_SetCursor(hi2c, x, y + 1);  // Move to the next page
    for (i = 8; i < 16; i++) {
        data = oled_font[char_index][i];  // Get font data
        OLED_WriteData(hi2c, data);
    }
}

// Display Chinese character
void OLED_DisplayChinese(I2C_HandleTypeDef *hi2c, uint8_t x, uint8_t y, ChineseCharInfo info) {
    uint8_t i, j;
    uint8_t width = chinese_fonts[info.font_index].width;
    uint8_t height = chinese_fonts[info.font_index].height;
    uint8_t pages = height / 8;
    uint16_t offset = info.char_index * width * pages;  // Calculate character data offset

    for (j = 0; j < pages; j++) {
        OLED_SetCursor(hi2c, x, y + j);  // Set cursor position
        for (i = 0; i < width; i++) {
            // Get bitmap data for display
            uint8_t data = chinese_fonts[info.font_index].data[offset + j * width + i];
            OLED_WriteData(hi2c, data);
        }
    }
}

// Display string (both Chinese and English)
void OLED_DisplayString(I2C_HandleTypeDef *hi2c, uint8_t x, uint8_t y, char *str)
{
    uint8_t i = 0;
    uint8_t current_x = x;

    // Check if the string is empty
    if (str == NULL) return;

    while (str[i] != '\0')
    {
        // Check if it is a Chinese character
        // If the character is within the GB2312 range, the first byte is in 0xA1 - 0xF7
        if ((uint8_t)str[i] >= 0xA1)
        {
            // Ensure no out - of - bounds access
            if (i + 1 >= strlen(str)) break;

            ChineseCharInfo info = GetChineseIndex(&str[i]);
            uint8_t width = chinese_fonts[info.font_index].width;

            // Display Chinese character
            OLED_DisplayChinese(hi2c, current_x, y, info);
            current_x += width;  // Move cursor by the width of the Chinese character
            i += 2;           // Chinese character occupies 2 bytes
        }
        else
        {
            // Display ASCII character
            OLED_DisplayChar(hi2c, current_x, y, str[i]);
            current_x += 8;   // Move cursor by the width of the ASCII character
            i++;
        }

        // Check if it exceeds the OLED width
        if (current_x >= OLED_WIDTH - 16) break;
    }
}

// Get the height of the currently used font
uint8_t GetFontHeight(ChineseCharInfo info) {
    return chinese_fonts[info.font_index].height;
}

// Display string centered
void OLED_DisplayStringCentered(I2C_HandleTypeDef *hi2c, uint8_t line, char *str)
{
    uint8_t x = 0;
    uint8_t i = 0;
    uint8_t width = 0;
    uint8_t max_font_height = 0;

    // Check if the string is empty
    if (str == NULL) return;

    // Calculate the total width and maximum font height of the string
    while (str[i] != '\0')
    {
        if ((uint8_t)str[i] >= 0xA1)
        {
            ChineseCharInfo info = GetChineseIndex(&str[i]);
            width += chinese_fonts[info.font_index].width;  // Accumulate Chinese character width
            uint8_t font_height = GetFontHeight(info);
            if (font_height > max_font_height) {
                max_font_height = font_height;
            }
            i += 2;       // Chinese character occupies 2 bytes
        }
        else
        {
            max_font_height = 16;
            width += 8;   // Accumulate ASCII character width
            i++;
        }
    }

    // Calculate y position
    uint8_t y = line * (max_font_height / 8);

    // Calculate x position for centering
    x = (OLED_WIDTH - width) / 2;

    // Display the string
    OLED_DisplayString(hi2c, x, y, str);
}

// Display Chinese string centered
void OLED_DisplayChineseStringCentered(I2C_HandleTypeDef *hi2c, uint8_t line, ChineseCharInfo *chinese_info, uint8_t length)
{
    uint8_t total_width = 0;
    for (uint8_t i = 0; i < length; i++) {
        total_width += chinese_fonts[chinese_info[i].font_index].width;
    }
    uint8_t x = (OLED_WIDTH - total_width) / 2;  // Calculate x position for centering
    uint8_t y = line * 2;  // Each line height is 16 pixels (2 pages)

    for (uint8_t i = 0; i < length; i++) {
        OLED_DisplayChinese(hi2c, x, y, chinese_info[i]);
        x += chinese_fonts[chinese_info[i].font_index].width;
    }
}

// Get Chinese character index
ChineseCharInfo GetChineseIndex(char *str) {
    ChineseCharInfo info = {0, 0};  // Default to the first character (corresponding to "明" maybe) if not matched

    // "温" (index 0)
    if (str[0] == 0xCE && str[1] == 0xC2) info.char_index = 0;
    // "度" (index 1)
    if (str[0] == 0xB6 && str[1] == 0xC8) info.char_index = 1;
    // "湿" (index 2)
    if (str[0] == 0xCA && str[1] == 0xAA) info.char_index = 2;
    // "欢" (index 3)
    if (str[0] == 0xBB && str[1] == 0xB6) info.char_index = 3;
    // "迎" (index 4)
    if (str[0] == 0xD3 && str[1] == 0xAD) info.char_index = 4;
    // "使" (index 5)
    if (str[0] == 0xCA && str[1] == 0xB9) info.char_index = 5;
    // "用" (index 6)
    if (str[0] == 0xD3 && str[1] == 0xC3) info.char_index = 6;
    // "天" (index 7)
    if (str[0] == 0xCC && str[1] == 0xEC) info.char_index = 7;
    // "su" (index 8)
    if (str[0] == 0xCB && str[1] == 0xDD) info.char_index = 8;
    // "du"
    if (str[0] == 0xA1 && str[1] == 0xE3) info.char_index = 9;
    // "温"
    if (str[0] == 0xBF && str[1] == 0xC5) info.char_index = 10;
    // "度"
    if (str[0] == 0xC1 && str[1] == 0xA3) info.char_index = 11;
    // "湿"
    if (str[0] == 0xCE && str[1] == 0xEF) info.char_index = 12;

    return info;
}
